#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <sys/stat.h>
#include "md5.h"
#include <pthread.h>
#include<sys/epoll.h>
#define LEN 128
#define MAX 128
typedef enum 
{
	UP = 1,
	DOWN,
	CMD
}Op;

typedef struct
{
	Op option;
	char data[LEN];  // ls -l    /  cd  ..  
}Data;

void UpFile(int c, Data val)
{
    char *path=val.data;    // filename##MD5:32409d400f0ed29e840b47ed72777c84
	 while(1)
	{
	 if(strncmp(path,"##MD5:",6)==0)
		 break;
	 path++;
	 }
	 char md5[16]={0};
	 strncpy(md5,path+6,16);
	 *path=0;
	 path=val.data;
	struct stat st;
	stat(path, &st);
   /************************计算MD5****************/
		unsigned char digest[16] = {0};
		MD5_CTX context;
		MD5Init(&context);//////////////////
    
        FILE *fp = fopen(path, "r");
        char *data = NULL;
        int ret = 0;

        if(!fp)
        {
            perror("fopen");
            exit(-1);
        }

        data = malloc(st.st_size);
        if(!data)
        {
            perror("malloc");
            exit(-1);
        }
        ret = fread(data, 1, st.st_size, fp);
        if(ret != st.st_size)
        {
            perror("fread");
            exit(-1);
        } 
        MD5Update(&context, data, st.st_size);///////////////
        fclose(fp);
        free(data);
    
    MD5Final(&context, digest);///////////////
   if(strncmp(digest,md5,16)==0)
   {
	  send(c,"ext",3,0);
	  return;
   
   }
    
	/****************************************/
	  int fd=open(path,O_RDWR|O_CREAT|O_TRUNC,0664);
	  if(fd==-1)
	{
	  send(c,"ERR",3,0);
	  return;
	  }
	  send(c,"OK",3,0);
	  while(1)
	{
		 char buff[1024]={0};
         
		 if(-1==recv(c,buff,1024,0))
		{
	 
		 break;
		}
		if(strncmp(buff,"##end",5)==0)
		{
	    
		  break;
		}
		 write(fd,buff,1024);

	}
}
void DownFile(int c, Data val)
{

  char *p=val.data;
  int filenamelength=0;
  while(strncmp(p,"##len",5))
	{
	  p++;
      filenamelength++;
	}

  char size[LEN]={0};
  strcpy(size,p+5);

  char filename[LEN]={0};
  strncpy(filename,val.data,filenamelength);
  printf("file already down %s\n",size);
  int fd=open(filename,O_RDONLY);
  if(fd==-1)
  {
  
	  send(c,"ER",2,0);
      return ;
  }
 
  struct stat st;
  fstat(fd,&st);

  char buff[128]="OK#";
  sprintf(buff+3,"%d",st.st_size);
  strcat(buff,"#");
  send(c,buff,strlen(buff),0);
  lseek(fd,atoi(size),SEEK_CUR);
   while(1)
   {
	   memset(buff,0,128);
        int n=read(fd,buff,127);
       if(n==0)
	   {
	   break;
	   }
	   if(-1==send(c,buff,n,0))
		   break;
   
   }


	close(c);
}

void ExecuteCmd(int c, Data val) //  ls   pwd   cd
{
	char buff[LEN] = {0};
	strcpy(buff, val.data);

	char *command[LEN] = {0};
	int count = 0;
	char *p = strtok(buff, " ");
	while(p != NULL)
	{
		command[count++] = p;
		p = strtok(NULL, " ");
	}

	if(strncmp(command[0], "cd", 2) == 0)
	{
		if(command[1] != NULL)
		{
			if(-1 == chdir(command[1]))
			{
				send(c, "ERR\n###", 7, 0);
			}
			else
			{
				send(c, "OK\n###", 6, 0);
			}
		}
		else
		{
			send(c, "ERR\n###", 7, 0);
		}
	}
	else
	{
		int fds[2];
		pipe(fds);
		pid_t n = fork();
		assert(n != -1);
		if(n == 0)
		{
			close(fds[0]);
			close(1);
			close(2);
			dup(fds[1]);
			dup(fds[1]);
			char path[LEN] = "/bin/";
			strcat(path, command[0]);
			execv(path, command);
			perror(" ");
			exit(0);
		}
		else
		{
			close(fds[1]);
			while(1)
			{
				char data[LEN] = {0};
				int n = read(fds[0], buff, LEN - 1);
				if(n <= 0)
				{
					close(fds[0]);
					send(c, "###", 3, 0);
					break;
				}

				send(c, buff, n, 0);
			}
		}
	}
}

void DisposeClient(int c) // 业务逻辑处理函数
{
	while(1)
	{
		Data val;
		memset(&val, 0, sizeof(val));
		int n = recv(c, &val, sizeof(val) - 1, 0);
		if(n <= 0)
		{
			close(c);
			break;
		}

		switch(val.option)
		{
			case UP:
				{
					UpFile(c, val);
					break;
				}
			case DOWN:
				{
					DownFile(c, val);
					break;
				}
			case CMD:
				{
					ExecuteCmd(c, val);
					break;
				}
			default:
				{
					break;
				}
		}
	}
}
int count=0;
void* tfx(void *arg)
{
    int spd=  *((int *)arg);
    
    
    struct epoll_event revents[MAX];
	struct epoll_event event;
	event.events=EPOLLIN;
	event.data.fd=spd;

	int epfd=epoll_create(5);
	assert(epfd!=-1);
	epoll_ctl(epfd,EPOLL_CTL_ADD,spd,&event);
	
	
	while(1)
	{
		int n=epoll_wait(epfd,revents,MAX,-1);
		assert(n!=-1);
		
         int i=0;
		for(;i<n;i++)
		{
		     int fd=revents[i].data.fd;
	        {
			
			   if(fd==spd)
			   {
				    int linkfd;
				   recv(spd,&linkfd,4,0);
			      struct epoll_event newevent;
				  newevent.events=EPOLLIN;
				  newevent.data.fd=linkfd;
				  epoll_ctl(epfd,EPOLL_CTL_ADD,linkfd,&newevent);
				  printf("one link is join one pthread!\n");
			   
			   }
			   else 
			   {
			   
				   count++;
			       DisposeClient(fd);
				   count--;  
			    
			   }
			
			
			}
		
		}

    }




}

void main()
{
	int listenfd = socket(AF_INET, SOCK_STREAM, 0);
	assert(listenfd != -1);

	struct sockaddr_in ser, cli;
	memset(&ser, 0, sizeof(ser));
	ser.sin_family = PF_INET;
	ser.sin_port = htons(6000);
	ser.sin_addr.s_addr = inet_addr("192.168.1.120");

	int res = bind(listenfd, (struct sockaddr*)&ser, sizeof(ser));
	assert(res != -1);

	listen(listenfd, 5);

	int mrr[3];
	int frr[3];
	for(int i=0;i<3;i++)
	{
		int arr[2];
	 if(0>socketpair(AF_LOCAL,SOCK_STREAM,0,arr))
	 {
	 printf("socketpair error\n");
	 
	 }
       mrr[i]=arr[0];
	   frr[i]=arr[1];

	}
   
	pthread_t pthid[3];
	for(int i=0;i<3;i++) //创建数量为3的线程池
	{
		pthread_create(&pthid[i],NULL,tfx,(void *)&frr[i]);
	   
	  
	}

    struct epoll_event revents[MAX];
	struct epoll_event event;
	event.events=EPOLLIN;
	event.data.fd=listenfd;
	int epfd=epoll_create(5);
	assert(epfd!=-1);
	epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&event);
	
	
	while(1)
	{
		int n=epoll_wait(epfd,revents,MAX,-1);
		assert(n!=-1);
		
         int i=0;
		for(;i<n;i++)
		{
		     int fd=revents[i].data.fd;
			 if(fd==listenfd)
			 {
				 
				 int len=sizeof(cli);
			   int linkfd=accept(fd,(struct  sockaddr*)&cli,&len);
			   
			     if(count>=3)
				 {
				   printf("sever is busying....plase wait!\n");
				 
				   close(linkfd);
				   continue;
				 }
			   
				 else
				  {
			   		printf("one cli is connect!\n");

			  	 	send(mrr[(count++)%3],&linkfd,4,0);
			       }
				   
			 }
		
		}
		

		//DisposeClient(linkfd);
	}

	close(listenfd);
}
